# coding=utf-8

import logging, pexpect, os
from config import BeetleConfig
from app.modules.common.pipelines import PipelineFactory
from app.modules.database.pipelines_mysql import ImportMysqlPipeLine
from app.modules.database.pipelines_oracle import InitImportOraclePipeLine, IncrementalImportOraclePipeLine
from app.modules.database.pipelines_cassandra import InitImportCassandraPipeLine

log = logging.getLogger(__name__)


class DatabasePipelinesFactory(PipelineFactory):

    def __init__(self, apps):
        self.apps = apps

    @staticmethod
    def build_pipeline_group(db_name, db_type):
        if db_name and db_type:
            return '%s_%s' % (db_name, db_type)
        else:
            return None

    @staticmethod
    def get_pipeline_name(appid, db_type, db_name, is_product=False):
        if appid and db_name:
            arr = str(appid).split(':')
            if len(arr) == 2:
                pip_prefix = 'pip-db'
                if is_product:
                    #return '%s:%s:%s:%s:product' % (pip_prefix, arr[0], db_type, db_name)
                    return '%s:%s:%s:product' % (pip_prefix, db_type, db_name)
                else:
                    #return '%s:%s:%s:%s:demo' % (pip_prefix, arr[0], db_type, db_name)
                    return '%s:%s:%s:demo' % (pip_prefix, db_type, db_name)
        raise RuntimeError(('Parameter error, appid : %s, db_name : %s, is_product : %s'
                            % (appid, db_name, is_product)))

    @staticmethod
    def resolve_pipeline_name(pipeline_name):
        """ return db_type, db_name, data_from """
        if pipeline_name:
            arr = str(pipeline_name).split(':')
            if len(arr) == 4:
                return arr[1], arr[2], arr[3]

        return None, None, None

    @staticmethod
    def get_local_ip():
        sh = '/bin/bash -c ' \
             '"/sbin/ifconfig -a|grep inet|grep -v 127.0.0.1|grep -v inet6|awk \'{print $2}\'|tr -d \"addr:\""'
        log.info('start execute shell : %s', sh)
        output, status = pexpect.run(sh, withexitstatus=1, timeout=30)
        log.info('=======> shell status: %s, local_ip: %s', status, output)
        if status == 0:
            return str(output).strip()
        else:
            raise RuntimeError('Get local IP address failed')

    def build_pipelines(self):
        ready_dbs = []
        pipelines = []
        if len(self.apps) > 0:
            local_ip = self.get_local_ip()
            for app in self.apps:
                if app.db_name and app.db_type:
                    key = str(app.db_name + ':' + app.db_type)
                    if key not in ready_dbs:
                        ready_dbs.append(key)
                        if app.db_type == 'mysql':
                            pipelines.append(self.__build_mysql_pipeline(app, is_product=True, local_ip=local_ip))
                            pipelines.append(self.__build_mysql_pipeline(app, is_product=False, local_ip=local_ip))
                        elif app.db_type == 'oracle':
                            pipelines.append(self.__build_oracle_pipeline(app, is_product=True, local_ip=local_ip))
                            pipelines.append(self.__build_oracle_pipeline(app, is_product=False, local_ip=local_ip))
                        elif app.db_type == 'cassandra':
                            pipelines.append(self.__build_cassandra_pipeline(app, is_product=True, local_ip=local_ip))
                            pipelines.append(self.__build_cassandra_pipeline(app, is_product=False, local_ip=local_ip))

        return pipelines

    def __build_mysql_pipeline(self, app, is_product=False, local_ip='127.0.0.1'):
        pipeline_name = self.get_pipeline_name(app.app_id, app.db_type, app.db_name, is_product=is_product)
        db_name = app.db_name

        temp_path = self.__build_temp_path(app.db_type)

        dump_path = self.__build_dump_path(is_product, app.db_type, db_name)

        sql_file = self.__build_default_after_sql_path(app.db_type, db_name)

        after_imp_sql_files = []
        if os.path.isfile(sql_file):
            after_imp_sql_files.append(sql_file)

        common_sql_file = self.__build_common_after_sql_path(app.db_type)
        if os.path.isfile(common_sql_file):
            after_imp_sql_files.append(common_sql_file)

        params = {'username': 'root',
                  'password': 'winit2015',
                  'temp_path': temp_path,
                  'dump_path': dump_path,
                  'after_imp_sql_files': after_imp_sql_files,
                  'db_name': db_name,
                  'local_ip': local_ip
                  }

        log.info("======> Init db pipeline : %s => %s", app.db_name, pipeline_name)
        group = DatabasePipelinesFactory.build_pipeline_group(db_name=app.db_name, db_type=app.db_type)
        return ImportMysqlPipeLine(pipeline_name, group=group, **params)

    def __build_oracle_pipeline(self, app, is_product=False, local_ip='127.0.0.1'):
        pipeline_name = self.get_pipeline_name(app.app_id, app.db_type, app.db_name, is_product=is_product)
        db_name = app.db_name

        username = db_name
        tablespace = db_name
        if str(db_name).upper() == 'ADEMPIERE':
            db_name = 'ERP'

        temp_path = self.__build_temp_path(app.db_type)

        dump_path = self.__build_dump_path(is_product, app.db_type, db_name)

        sql_file = self.__build_default_after_sql_path(app.db_type, db_name)
        after_imp_sql_files = []
        if os.path.isfile(sql_file):
            after_imp_sql_files.append(sql_file)

        common_sql_file = self.__build_common_after_sql_path(app.db_type)
        if os.path.isfile(common_sql_file):
            after_imp_sql_files.append(common_sql_file)

        if str(db_name).upper() == 'SMS':
            source_username = 'sms'
        else:
            source_username = 'adempiere'

        params = {'username': username,
                  'password': 'adempiere',
                  'tablespace': tablespace,
                  'temp_path': temp_path,
                  'dump_path': dump_path,
                  'source_username': source_username,
                  'source_tablespace': 'TS_ADEMPIERE',
                  'after_imp_sql_files': after_imp_sql_files,
                  'db_name': db_name,
                  'local_ip': local_ip
                  }

        log.info("======> Init db pipeline : %s => %s", app.db_name, pipeline_name)
        group = DatabasePipelinesFactory.build_pipeline_group(db_name=app.db_name, db_type=app.db_type)
        if is_product:
            return IncrementalImportOraclePipeLine(pipeline_name, group=group, **params)
        else:
            return InitImportOraclePipeLine(pipeline_name, group=group, **params)

    def __build_cassandra_pipeline(self, app, is_product=False, local_ip='127.0.0.1'):
        pipeline_name = self.get_pipeline_name(app.app_id, app.db_type, app.db_name, is_product=is_product)
        temp_path = self.__build_temp_path(app.db_type)

        dump_path = self.__build_dump_path(is_product, app.db_type, app.db_name)

        sql_file = self.__build_default_after_sql_path(app.db_type, app.db_name)
        after_imp_sql_files = []
        if os.path.isfile(sql_file):
            after_imp_sql_files.append(sql_file)

        common_sql_file = self.__build_common_after_sql_path(app.db_type)
        if os.path.isfile(common_sql_file):
            after_imp_sql_files.append(common_sql_file)

        params = {'cassandra_path': BeetleConfig.CASSANDRA_HOME_PATH,
                  'username': 'cassandra',
                  'password': 'cassandra',
                  'temp_path': temp_path,
                  'dump_path': dump_path,
                  'keyspace': app.db_name,
                  'after_imp_sql_files': after_imp_sql_files,
                  'local_ip': local_ip
                  }

        log.info("======> Init db pipeline : %s => %s", app.db_name, pipeline_name)
        group = DatabasePipelinesFactory.build_pipeline_group(db_name=app.db_name, db_type=app.db_type)
        return InitImportCassandraPipeLine(pipeline_name, group=group, **params)

    def __build_dump_path(self, is_product, db_type, db_name):
        if is_product:
            dump_path = '%s/%s/%s' % (BeetleConfig.DUMP_PRODUCT_BASE_PATH, db_type, db_name)
        else:
            dump_path = '%s/%s/%s' % (BeetleConfig.DUMP_DEMO_BASE_PATH, db_type, db_name)
        return dump_path

    def __build_default_after_sql_path(self, db_type, db_name):
        return '%s/%s/%s.sql' % (BeetleConfig.DB_SCRIPT_PATH, db_type, db_name)

    def __build_common_after_sql_path(self, db_type):
        return '%s/common/%s.sql' % (BeetleConfig.DB_SCRIPT_PATH, db_type)

    def __build_temp_path(self, db_type):
        return '%s/%s' % (BeetleConfig.TEMP_PATH, db_type)


